// -*-mode:c++; coding:utf-8-*-

#ifndef _XBASE_PROTOCOL_HPP_
#define _XBASE_PROTOCOL_HPP_

#include <boost/function.hpp>

#include "protocol_base.hpp"
#include "protocol_struct.hpp"
#include "protocol_packet.hpp"

namespace xbase
{
	namespace protocol
	{
		namespace detail
		{
			template<typename OArchive,
				 type_code type>
			struct save_function
			{
				typedef bool (*save_t)(OArchive &out);
				
				typedef typename packet_type<type>::type packet_type;
				
				const packet_type &m_packet;
				
				explicit
				save_function(const packet &p)
					: m_packet(*boost::polymorphic_downcast<const packet_type*>(&p)) {
					assert(p.get_type() == type);
				}

				save_function(const save_function &other)
					: m_packet(other.m_packet) {}
				
				typedef bool result_type;
				
				bool operator()(OArchive &out) const {
					return m_packet.save(out);
				}
			};
		} // namespace detail

#define case_save_function(type) case type:				\
		return detail::save_function<OArchive, type>(p)

		template<typename OArchive>
		boost::function<bool(OArchive&)> get_save_function(const packet &p) {
			switch(p.get_type())
			{
				case_save_function(ROOT_META_REQ);
				case_save_function(ROOT_META_RESP);
				case_save_function(WRITE_REQ);
				case_save_function(WRITE_RESP);
				case_save_function(TABLE_INFO_REQ);
				case_save_function(TABLE_INFO_RESP);
				case_save_function(SCAN_OPEN_REQ);
				case_save_function(SCAN_OPEN_RESP);
				case_save_function(SCAN_NEXT_REQ);
				case_save_function(SCAN_NEXT_RESP);
				case_save_function(SCAN_CLOSE_REQ);
				case_save_function(SCAN_CLOSE_RESP);
				case_save_function(BATCH_WRITE_REQ);
				case_save_function(BATCH_WRITE_RESP);
				case_save_function(TABLET_SERVER_REPORT);
				case_save_function(HEARTBEAT_REQ);
				case_save_function(LOAD_TABLET_REQ);
				case_save_function(LOAD_TABLET_RESP);
				case_save_function(REMOVE_TABLET_REQ);
				case_save_function(REMOVE_TABLET_RESP);
				case_save_function(TABLET_SPLIT_REPORT);
				case_save_function(TABLET_SERVER_QUIT);
				case_save_function(LIST_TABLET_REQ);
				case_save_function(LIST_TABLET_RESP);
				case_save_function(CREATE_TABLE_REQ);
				case_save_function(CREATE_TABLE_RESP);
				case_save_function(DELETE_TABLE_REQ);
				case_save_function(DELETE_TABLE_RESP);
				case_save_function(READ_REQ);
				case_save_function(READ_RESP);
				case_save_function(LIST_TABLE_REQ);
				case_save_function(LIST_TABLE_RESP);

				// ...
				
			case P_BAD_TYPE_CODE:
			default:
				return detail::save_function<OArchive, P_BAD_TYPE_CODE>(p);
			}
		}
		// usage:
		// boost::function<bool(OArchive&)> f =
		//     get_save_function<OArchive>(packet);
		// f(out);
			
#define case_decode(t) case t: do {					\
			typedef packet_type<t> _t;			\
			_t::ptr_type p(new _t::type());			\
			if(p->load(in))					\
				ptr = p;				\
			else						\
				ec = error::packet_decode_failed;	\
			return ec;					\
		} while(false)
		
		template<typename IArchive>
		boost::system::error_code new_packet_and_load(packet_ptr &ptr,
							      boost::system::error_code &ec,
							      IArchive &in)
		{
			ec.clear();

			// get type_code
			IArchive in_copy = in;
			uint16_t type_value = P_BAD_TYPE_CODE;
			bool ok = (in_copy.seek(packet::POS_TYPE, SEEK_CUR) &&
				   in_copy.load(&type_value));
			if(! ok)
			{
				ec = error::header_incomplete;
				return ec;
			}

			const type_code type = static_cast< type_code > (type_value);
			switch(type)
			{
				case_decode(ROOT_META_REQ);
				case_decode(ROOT_META_RESP);
				case_decode(WRITE_REQ);
				case_decode(WRITE_RESP);
				case_decode(TABLE_INFO_REQ);
				case_decode(TABLE_INFO_RESP);
				case_decode(SCAN_OPEN_REQ);
				case_decode(SCAN_OPEN_RESP);
				case_decode(SCAN_NEXT_REQ);
				case_decode(SCAN_NEXT_RESP);
				case_decode(SCAN_CLOSE_REQ);
				case_decode(SCAN_CLOSE_RESP);
				case_decode(BATCH_WRITE_REQ);
				case_decode(BATCH_WRITE_RESP);
				case_decode(TABLET_SERVER_REPORT);
				case_decode(HEARTBEAT_REQ);
				case_decode(LOAD_TABLET_REQ);
				case_decode(LOAD_TABLET_RESP);
				case_decode(REMOVE_TABLET_REQ);
				case_decode(REMOVE_TABLET_RESP);
				case_decode(TABLET_SPLIT_REPORT);
				case_decode(TABLET_SERVER_QUIT);
				case_decode(LIST_TABLET_REQ);
				case_decode(LIST_TABLET_RESP);
				case_decode(CREATE_TABLE_REQ);
				case_decode(CREATE_TABLE_RESP);
				case_decode(DELETE_TABLE_REQ);
				case_decode(DELETE_TABLE_RESP);
				case_decode(READ_REQ);
				case_decode(READ_RESP);
				case_decode(LIST_TABLE_REQ);
				case_decode(LIST_TABLE_RESP);
			case P_BAD_TYPE_CODE:
			default:
				ec = error::bad_packet_type;
				return ec;
			}
		}

#undef case_decode

	} // namespace protocol

} // namespace xbase

#endif	// _XBASE_PROTOCOL_HPP_

